package com.zxjbyte.yiyi.extension.job.manager;

import cn.hutool.core.collection.CollUtil;
import com.zxjbyte.yiyi.extension.job.core.JobKeyBuilder;
import com.zxjbyte.yiyi.extension.job.core.ObjectJobCreationArgs;
import com.zxjbyte.yiyi.extension.job.core.ObjectJobUpdateArgs;
import com.zxjbyte.yiyi.framework.common.exception.ServiceException;
import com.zxjbyte.yiyi.framework.common.util.ValidationUtil;
import com.zxjbyte.yiyi.extension.job.core.processor.BaseInvokingJob;
import com.zxjbyte.yiyi.extension.job.core.trigger.TriggerArgs;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.quartz.*;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Objects;

/**
 * Scheduler管理器
 *
 * @Author zhangxingjia
 * @Date 2023/9/6 19:35
 * @Version: 1.0
 */
@Component
@RequiredArgsConstructor
public class QuartzScheduleManager {

    private final Scheduler scheduler;

    /**
     * 添加任务
     * @param args
     */
    @SneakyThrows
    public void addJob(ObjectJobCreationArgs args){
        // 创建 JobDetail
        JobDetail jobDetail = buildJobDetail(args.getJobKey(), args.getJobDescription());
        // 创建 Trigger
        Trigger trigger = buildTrigger(args);
        // 添加监听器
        addListener(scheduler.getListenerManager(), args.getJobListenerList(),
                args.getTriggerListenerList(), args.getSchedulerListenerList());
        // 新增调度
        scheduler.scheduleJob(jobDetail, trigger);
    }

    /**
     * 更新任务触发器规则
     * @param args
     */
    @SneakyThrows
    public void updateJobTrigger(ObjectJobUpdateArgs args){
        // 创建 Trigger
        Trigger newTrigger = buildTrigger(args);
        // 移除（删除）给定键的触发器，并存储新的给定触发器
        scheduler.rescheduleJob(args.getOldTriggerKey(), newTrigger);
    }

    /**
     * 暂停特定的作业实例
     * @param jobKey
     */
    @SneakyThrows
    public void pauseJob(JobKey jobKey){
        scheduler.pauseJob(jobKey);
    }

    /**
     * 暂停特定的作业实例
     * @param jobName
     * @param jobGroup
     */
    public void pauseJob(String jobName, String... jobGroup){
        this.pauseJob(JobKeyBuilder.newJobKey(jobName, jobGroup));
    }

    /**
     * 重新启动已暂停的作业
     * @param jobKey
     */
    @SneakyThrows
    public void resumeJob(JobKey jobKey){
        scheduler.resumeJob(jobKey);
    }

    /**
     * 重新启动已暂停的作业
     * @param jobName
     * @param jobGroup
     */
    public void resumeJob(String jobName, String... jobGroup){
        this.resumeJob(JobKeyBuilder.newJobKey(jobName, jobGroup));
    }

    /**
     * 删除任务
     * @param jobKey
     */
    @SneakyThrows
    public void deleteJob(JobKey jobKey){
        scheduler.deleteJob(jobKey);
    }

    /**
     * 删除任务
     * @param jobName
     * @param jobGroup
     */
    public void deleteJob(String jobName, String... jobGroup){
        this.deleteJob(JobKeyBuilder.newJobKey(jobName, jobGroup));
    }

    /**
     * 可变参数过去作业key组名
     * @param jobGroup
     * @return
     */
    private String getJobGroup(String... jobGroup){
        if(jobGroup != null && jobGroup.length >= 1){
            return jobGroup[0];
        }
        return null;
    }

    /**
     * 添加监听器
     * @param listenerManager
     * @param jobListenerList
     * @param triggerListenerList
     * @param schedulerListenerList
     */
    private void addListener(ListenerManager listenerManager, List<Object[]> jobListenerList, List<Object[]> triggerListenerList,
                             List<SchedulerListener> schedulerListenerList){
        //将给定的JobListener添加到Scheduler，并注册它以接收由任意给定匹配器匹配的作业的事件
        if(CollUtil.isNotEmpty(jobListenerList)){
            jobListenerList.forEach(s ->{
                JobListener jobListener = (JobListener) s[0];
                List<Matcher<JobKey>> jobMatchers = (List<Matcher<JobKey>>) s[1];
                listenerManager.addJobListener(jobListener, jobMatchers);
            });
        }
        //将给定的TriggerListener添加到Scheduler，并将其注册以接收由任意给定匹配器匹配的触发器的事件
        if(CollUtil.isNotEmpty(triggerListenerList)){
            triggerListenerList.forEach(s ->{
                TriggerListener triggerListener = (TriggerListener) s[0];
                List<Matcher<TriggerKey>> jobMatchers = (List<Matcher<TriggerKey>>) s[1];
                listenerManager.addTriggerListener(triggerListener, jobMatchers);
            });
        }
        //向Scheduler注册给定的SchedulerListener。
        if(CollUtil.isNotEmpty(schedulerListenerList)){
            schedulerListenerList.forEach(listenerManager::addSchedulerListener);
        }
    }

    /**
     * 构建触发器
     * @param args
     * @return
     */
    @SneakyThrows
    private Trigger buildTrigger(TriggerArgs args){
        ValidationUtil.assertNotNull(args.getScheduleBuilder(), "Trigger ScheduleBuilder");
        TriggerKey triggerKey = args.getTriggerKey();
        ValidationUtil.assertNotNull(triggerKey, "TriggerKey");
        if(scheduler.checkExists(triggerKey)){
            throw new ServiceException("Trigger with name: '" + triggerKey.getName() + "' and group: '" +
                    triggerKey.getGroup() + "', because one already exists.");
        }
        TriggerBuilder<? extends Trigger> triggerBuilder = TriggerBuilder.newTrigger()
                .withSchedule(args.getScheduleBuilder()).withIdentity(triggerKey);
        if (!args.getJobDataMap().isEmpty()) {
            triggerBuilder.usingJobData(args.getJobDataMap());
        }
        // 设置触发器描述
        if(Objects.nonNull(args.getTriggerDescription())){
            triggerBuilder.withDescription(args.getTriggerDescription());
        }
        // 设置触发器的开始时间
        if (args.isTriggerStartNow()) {
            triggerBuilder.startNow(); // 开始时间为当前时间
        } else if (Objects.nonNull(args.getTriggerStartTime())) {
            triggerBuilder.startAt(args.getTriggerStartTime());
        }
        // 设置触发器的结束时间
        if (Objects.nonNull(args.getTriggerEndTime())) {
            triggerBuilder.endAt(args.getTriggerEndTime());
        }
        return triggerBuilder.build();
    }

    /**
     * 构建 JobDetail
     * @param jobKey
     * @param jobDescription
     * @return
     */
    @SneakyThrows
    private JobDetail buildJobDetail(JobKey jobKey, String jobDescription){
        ValidationUtil.assertNotNull(jobKey, "JobKey");
        // 检查JobKey是否存在
        if(scheduler.checkExists(jobKey)){
            throw new ServiceException("JobKey with name: '" + jobKey.getName() + "' and group: '" +
                    jobKey.getGroup() + "', because one already exists.");
        }
        JobBuilder jobBuilder = JobBuilder.newJob(BaseInvokingJob.class).withIdentity(jobKey);
        if (Objects.nonNull(jobDescription)) {
            jobBuilder.withDescription(jobDescription);
        }
        return jobBuilder.build();
    }
}
